#include "../redgpu_f.h"
#include "../src/glfw/include/GLFW/glfw3.h"
#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS
#include "../ofxaddons/cimgui/cimgui.h"
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

int                           g_leftMouseButtonIsPressed;
int                           g_currentCam;
RedFHandleFirstPersonCamera * g_cams;
RedFHandleManipulator       * g_man;
RedFHandleMesh              * g_mesh;
RedFHandleImage             * g_image;
RedFHandleImgui             * g_imgui;
RedFHandleEquiMap           * g_equi;
RedFHandleFbo               * g_fbo;
RedFHandleImage             * g_shaderImage;
RedFHandleShader            * g_shader;
RedFHandleAssimp            * g_assimp;
RedFHandleMesh              * g_assimpMesh;

void setup() {
  redFSetEscapeQuitsApp(1);
  redFDisableArbTex(); // NOTE(Constantine): Helps matcap, but disables equi map rendering, unfortunately.

  g_leftMouseButtonIsPressed = 0;
  g_currentCam = 0;
  g_cams = redFCreateFirstPersonCamera(2);
  g_man = redFCreateManipulator(1);
  g_mesh = redFCreateMesh(1);
  g_image = redFCreateImage(1);
  g_imgui = redFCreateImgui(1);
  g_equi = redFCreateEquiMap(1);
  g_fbo = redFCreateFbo(1);
  g_shaderImage = redFCreateImage(1);
  g_shader = redFCreateShader(1);
  g_assimp = redFCreateAssimp(1);
  g_assimpMesh = redFCreateMesh(1);

  redFCameraSetFov(redFFirstPersonCameraCastToCamera(g_cams[0]), 90);
  redFCameraSetFov(redFFirstPersonCameraCastToCamera(g_cams[1]), 90);

  redFManipulatorSetManipulatorScale(g_man[0], 1.35f);

  redFMeshSetMode(g_mesh[0], REDF_PRIMITIVE_MODE_TRIANGLE_STRIP);
  redFMeshAddVertex(g_mesh[0], 0, 0, 0);
  redFMeshAddVertex(g_mesh[0], 400, 0, 0);
  redFMeshAddVertex(g_mesh[0], 400, 400, 0);
  redFMeshAddVertex(g_mesh[0], 0, 400, 0);
  redFMeshAddVertex(g_mesh[0], 0, 0, 0);
  redFMeshAddTexCoord(g_mesh[0], 0, 2048);
  redFMeshAddTexCoord(g_mesh[0], 2048, 2048);
  redFMeshAddTexCoord(g_mesh[0], 2048, 0);
  redFMeshAddTexCoord(g_mesh[0], 0, 0);
  redFMeshAddTexCoord(g_mesh[0], 0, 2048);

  redFImageAllocate(g_image[0], 2048, 2048, REDF_IMAGE_TYPE_COLOR_RGB);
  for (int y = 0; y < redFImageGetHeight(g_image[0]); y += 1) {
    for (int x = 0; x < redFImageGetWidth(g_image[0]); x += 1) {
      float v = fabs(sin((float)(x + y) / 18.f)) * 255.f;
      redFImageSetColor(g_image[0], x, y, v, v, v, 255);
    }
  }
  redFImageUpdate(g_image[0]);
  redFImageSetTextureMinMagFilter(g_image[0], /*GL_NEAREST*/0x2600, /*GL_NEAREST*/0x2600);

  redFImguiSetup(g_imgui[0]);

  redFEquiMapSetup(g_equi[0], 2048);

  redFFboAllocate(g_fbo[0], 2048, 2048, NULL);

#ifdef _WIN32
  redFShaderLoad(g_shader[0], "C:/Users/iamvf/Desktop/redgpu_f/example/matcap.vs", "C:/Users/iamvf/Desktop/redgpu_f/example/matcap.fs");
#endif
#ifdef __linux__
  redFShaderLoad(g_shader[0], "/home/constantine/Documents/redgpu_f/example/matcap.vs", "/home/constantine/Documents/redgpu_f/example/matcap.fs");
#endif

#ifdef _WIN32
  redFImageLoad(g_shaderImage[0], "C:/Users/iamvf/Desktop/redgpu_f/example/matcap.jpg");
#endif
#ifdef __linux__
  redFImageLoad(g_shaderImage[0], "/home/constantine/Documents/redgpu_f/example/matcap.jpg");
#endif

#ifdef _WIN32
  redFAssimpLoadModel(g_assimp[0], "C:/Users/iamvf/Desktop/redgpu_f/example/snowden.obj", 0);
#endif
#ifdef __linux__
  redFAssimpLoadModel(g_assimp[0], "/home/constantine/Documents/redgpu_f/example/snowden.obj", 0);
#endif
  redFAssimpGetMesh(g_assimp[0], 0, g_assimpMesh[0]);
}

void scene(void * userData) {
  redFBackground(175, 175, 175, 255);
  if (g_leftMouseButtonIsPressed == 1 && redFFirstPersonCameraControlIsEnabled(g_cams[g_currentCam]) == 1) {
    float x = redFNodeGetGlobalPositionX(redFFirstPersonCameraCastToNode(g_cams[(g_currentCam + 1) % 2]));
    float y = redFNodeGetGlobalPositionY(redFFirstPersonCameraCastToNode(g_cams[(g_currentCam + 1) % 2]));
    float z = redFNodeGetGlobalPositionZ(redFFirstPersonCameraCastToNode(g_cams[(g_currentCam + 1) % 2]));
    redFNodeLookAt(redFFirstPersonCameraCastToNode(g_cams[g_currentCam]), x, y, z, 0, 1, 0);
  }
  if (userData != NULL) {
    redFFirstPersonCameraBegin(g_cams[g_currentCam]);
  }
  redFEnableDepthTest();
  {
    redFPushMatrix();
    redFPushStyle();
    redFFill();
    redFEnableLighting();
    redFShaderBegin(g_shader[0]);
    redFShaderSetUniformImage(g_shader[0], "litsphereTexture", g_shaderImage[0], 1);
    redFRotateDeg(90, 1, 0, 0);
    redFRotateDeg(180, 0, 0, 1);
    redFScale(2, 2, 2);
    redFMeshDraw(g_assimpMesh[0]);
    redFShaderEnd(g_shader[0]);
    redFDisableLighting();
    redFPopStyle();
    redFPopMatrix();
  }
  redFNoFill();
  redFDrawSphere(0.0, 0.0, 0, 200);
  redFDrawSphere(500, 400, 0, 200);
  redFDrawSphere(-500, -400, 0, 200);
  redFDrawSphere(500, -400, 0, 200);
  redFSetLineWidth(5);
  redFDrawLine(0, 0, 0, 1, 1, 1);
  redFSetLineWidth(1);
  for (int i = 0; i < 2; i += 1) {
    if (i == g_currentCam) {
      continue;
    }
    redFNodeDraw(redFFirstPersonCameraCastToNode(g_cams[i]));
    redFCameraDrawFrustum(redFFirstPersonCameraCastToCamera(g_cams[i]));
  }
  redFPushMatrix();
  {
    float s[3];
    redFManipulatorGetScale(g_man[0], s);
    float r[4];
    redFManipulatorGetRotationAxisAngleDeg(g_man[0], r);
    float t[3];
    redFManipulatorGetTranslation(g_man[0], t);
    redFTranslate(t[0], t[1], t[2]);
    redFRotateDeg(r[3], r[0], r[1], r[2]);
    redFScale(s[0], s[1], s[2]);
    if (userData != NULL) {
      redFFboBind(g_fbo[0], 0);
      redFMeshDraw(g_mesh[0]);
      redFFboUnbind(g_fbo[0], 0);
    } else {
      redFImageBind(g_image[0]);
      redFMeshDraw(g_mesh[0]);
      redFImageUnbind(g_image[0]);
    }
  }
  redFPopMatrix();
  redFManipulatorDraw(g_man[0], redFFirstPersonCameraCastToCamera(g_cams[g_currentCam]));
  redFDisableDepthTest();
  if (userData != NULL) {
    redFFirstPersonCameraEnd(g_cams[g_currentCam]);
    for (int i = 0; i < 2; i += 1) {
      if (i == g_currentCam) {
        continue;
      }
      float x = redFNodeGetGlobalPositionX(redFFirstPersonCameraCastToNode(g_cams[i]));
      float y = redFNodeGetGlobalPositionY(redFFirstPersonCameraCastToNode(g_cams[i]));
      float z = redFNodeGetGlobalPositionZ(redFFirstPersonCameraCastToNode(g_cams[i]));
      redFDrawBitmapStringHighlightFromCamera("Camera", x, y, z, 0, 0, 0, 175, 255, 255, 255, 255, redFFirstPersonCameraCastToCamera(g_cams[g_currentCam]));
    }
    redFImguiBegin(g_imgui[0]);
    ImVec2 v = {};
    if (igButton("Hello", v)) {
      printf("Hello button is pressed!\n");
      fflush(stdout);
    }
    redFImguiEnd(g_imgui[0]);
  }
}

void theexit() {
  redFDestroyFirstPersonCamera(g_cams);
  redFDestroyManipulator(g_man);
  redFDestroyMesh(g_mesh);
  redFDestroyImage(g_image);
  redFDestroyEquiMap(g_equi);
  redFDestroyFbo(g_fbo);
  redFDestroyImage(g_shaderImage);
  redFDestroyShader(g_shader);
  redFDestroyAssimp(g_assimp);
  redFDestroyMesh(g_assimpMesh);
}

void update() {
  glfwPollEvents();
}

void draw() {
  redFEquiMapRender(g_equi[0], scene, NULL, -0.209871, 14.360579, 21.426233);
  redFFboBegin(g_fbo[0], 1);
  redFClear(0, 0, 0, 0);
  redFEquiMapDraw(g_equi[0], 0, 0, 2048, 2048);
  redFFboEnd(g_fbo[0]);
  redFPushStyle();
  scene((void *)1);
  redFPopStyle();
}

void mousePressed(RedFHandleEventParametersMouse parameters) {
  if (redFEventParametersMouseGetButton(parameters) == REDF_MOUSE_BUTTON_LEFT) {
    g_leftMouseButtonIsPressed = 1;
  }
  if (redFEventParametersMouseGetButton(parameters) == REDF_MOUSE_BUTTON_MIDDLE) {
    RedFBool32 isEnabled = redFFirstPersonCameraControlIsEnabled(g_cams[g_currentCam]);
    redFFirstPersonCameraControlDisable(g_cams[g_currentCam]);
    g_currentCam += 1;
    g_currentCam %= 2;
    if (isEnabled == 1) {
      redFFirstPersonCameraControlEnable(g_cams[g_currentCam]);
    }
  }
  if (redFEventParametersMouseGetButton(parameters) == REDF_MOUSE_BUTTON_RIGHT) {
    redFFirstPersonCameraControlToggle(g_cams[g_currentCam]);
    if (redFFirstPersonCameraControlIsEnabled(g_cams[g_currentCam]) == 1) {
      redFManipulatorDisable(g_man[0]);
    } else {
      redFManipulatorEnable(g_man[0]);
    }
  }
}

void mouseReleased(RedFHandleEventParametersMouse parameters) {
  if (redFEventParametersMouseGetButton(parameters) == REDF_MOUSE_BUTTON_LEFT) {
    g_leftMouseButtonIsPressed = 0;
  }
}

void keyPressed(RedFHandleEventParametersKey parameters) {
  switch (redFEventParametersKeyGetKeycode(parameters)) {
    case (49): redFManipulatorToggleTranslation(g_man[0]); break;
    case (50): redFManipulatorToggleRotation(g_man[0]); break;
    case (51): redFManipulatorToggleScale(g_man[0]); break;
  }
}

int main() {
  RedFEvents events = {};
  events.setup         = setup;
  events.exit          = theexit;
  events.update        = update;
  events.draw          = draw;
  events.mousePressed  = mousePressed;
  events.mouseReleased = mouseReleased;
  events.keyPressed    = keyPressed;
  return redFMain(&events, 1920, 1080, REDF_WINDOW_MODE_WINDOW, 0, 1, 1, 8, 0, 0);
}
